package com.dh.spring.client.bootstrap;

import com.alibaba.fastjson.JSON;
import com.dh.spring.client.file.common.SendEntity;
import com.dh.spring.client.handler.ChannelHandler;
import com.dh.spring.client.http.*;
import com.dh.spring.common.BasicConstant;
import com.dh.spring.common.HttpRequest;
import com.dh.spring.common.HttpResponse;
import com.dh.spring.util.BasicUtil;
import com.dh.spring.util.HttpRequestAnalysisUtil;
import com.dh.spring.util.LocalTime;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * TODO 处理客户端http请求解析(即OP_ACCEPT和OP_CONNECT两类处理事件)
 * */
@Slf4j
@SuppressWarnings("all")
public class ServerSocketChannelHttpRequestHandler implements ChannelHandler {
    private static ServerSocketChannel serverSocketChannel = null;
    private String channelServerName = "";
    private static SendEntity sendEntity = new SendEntity();
    public static Map<String, LinkedBlockingQueue<FileExecStatus>> fileExecStatusMap = new ConcurrentHashMap<String, LinkedBlockingQueue<FileExecStatus>>(100);

    public ServerSocketChannelHttpRequestHandler(String channelServerName) {
        this.channelServerName = channelServerName;
        fileExecStatusMap.put(BasicConstant.UPLOAD, new LinkedBlockingQueue<FileExecStatus>(100));
        fileExecStatusMap.put(BasicConstant.DOWNLOAD, new LinkedBlockingQueue<FileExecStatus>(100));
        fileExecStatusMap.put(BasicConstant.LINUX_COMMAND, new LinkedBlockingQueue<FileExecStatus>(100));
        this.start();
    }

    //TODO 启动http客户端请求监听服务
    public void start() {
        log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] ServerSocketChannelHttpRequestHandler | --> 开启客户端监听通道服务，监听地址：IP = {}, PORT = {}", GlobalNIOClientListenerDelegate.SOCKET_CLIENT_IP, GlobalNIOClientListenerDelegate.SOCKET_CLIENT_PORT);

        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(InetAddress.getByName(GlobalNIOClientListenerDelegate.SOCKET_CLIENT_IP), GlobalNIOClientListenerDelegate.SOCKET_CLIENT_PORT));
            serverSocketChannel.configureBlocking(false);
            sendEntity.setChannelName(this.channelServerName);
            serverSocketChannel.register(GlobalNIOClientBootstrap.open(), SelectionKey.OP_ACCEPT).attach(sendEntity);
        } catch (IOException e) {
            try {
                serverSocketChannel.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            } finally {
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] ServerSocketChannelHttpRequestHandler | --> 文件客户端监听通道服务开启异常，将关闭文件客户端，error = {}", e.getMessage());
            }
        }
    }

    //TODO ServerSocketHandler通道任务处理
    @Override
    public void handler(SelectionKey selectionKey) {
        if(selectionKey.isAcceptable()) {
            acceptedHandler(selectionKey);
        }
    }

    //TODO 监听Http请求并创建对应SocketChannel进行处理
    private void acceptedHandler(SelectionKey selectionKey) {
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
        SocketChannel socketChannel = null;
        try {
            //TODO 接收http连接
            socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            this.connectedHandler(socketChannel);
        } catch (IOException e) {
            FileCoreServiceHandler.closeSocketChannel(selectionKey, null, "ServerSocketChannelHttpRequestHandler","客户端处理文件http请求异常, 已关闭当前http请求连接, error = " + e.getMessage(), "", "", 0);
        }
    }

    //TODO 处理监听的http请求SocketChannel三次握手连接情况后续处理
    private void connectedHandler(SocketChannel socketChannel) {
        try {
            while (true) {
                if(socketChannel.finishConnect()) {
                    break;
                }
            }

            if(socketChannel.isConnected()) {
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] ServerSocketChannelHttpRequestHandler | --> 当前http连接：" + ((InetSocketAddress) socketChannel.getRemoteAddress()).getAddress().getHostAddress() + " -----------");
                //TODO 向线程池提交HttpRequest请求处理任务
                GlobalNIOClientBootstrap.executorService.submit(new HttpRequestHandlerRunnable(socketChannel));
            }
        } catch (IOException e) {
            if(null != socketChannel) {
                try {
                    socketChannel.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            log.error("[" + LocalTime.formatDate(LocalDateTime.now()) + "] ServerSocketChannelHttpRequestHandler | --> 客户端http连接异常, 已关闭http请求连接, error = {}", e.getMessage());
        }
    }

    //TODO Http请求处理线程
    static class HttpRequestHandlerRunnable implements Runnable {
        private SocketChannel socketChannel;
        private HttpResponse httpResponse = null;

        public HttpRequestHandlerRunnable(SocketChannel socketChannel) {
            this.socketChannel = socketChannel;
        }

        @Override
        public void run() {

            try {
                StringBuffer requestData = new StringBuffer("");
                //TODO 默认分配1KB(1024B)进行读取数据，如果读取全部占满，还需下次继续循环读取数据,如果无法占满，说明请求数据小于1KB，则跳出循环
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
                while(this.socketChannel.read(byteBuffer) > 0) {
                    byteBuffer.flip();
                    byte[] bytes = new byte[byteBuffer.remaining()];
                    while(byteBuffer.hasRemaining()) {
                        byteBuffer.get(bytes, 0, bytes.length);
                        requestData.append(new String(bytes, 0, bytes.length));
                    }
                    byteBuffer.clear();
                }

                this.httpResponse = this.parseHttpRequest(requestData);
            } catch (Exception e) {
                this.httpResponse.getBodyData().append("ServerSocketChannelHttpRequestHandler ---> Error = 客户端解析文件http请求数据异常，" + e.getMessage());
                log.error("[" + LocalTime.formatDate(LocalDateTime.now()) + "] ServerSocketChannelHttpRequestHandler | --> 客户端解析文件http请求数据异常, 已关闭当前http请求连接, error = {}", e.getMessage());
            } finally {
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] ServerSocketChannelHttpRequestHandler | --> 客户端解析文件http请求处理结果: result = {}", this.httpResponse.getBodyData().toString());
                if(null != this.socketChannel) {
                    try {
                        //TODO --> 向客户端发送http响应数据
                        StringBuffer stringBuffer = new StringBuffer("");
                        stringBuffer.append("响应结果: --> \r\n").append("HTTP/1.1 200 OK\r\n").append("Server: arthas-client/1.0\r\n").append("Content-Type: " + this.httpResponse.getContentType() + "\r\n").append("Content-Lenth: " + this.httpResponse.getBodyData().toString().getBytes(Charset.forName("UTF-8")).length + "\r\n").append("Last-Modified: " + HttpResponse.sdf.format(new Date()) + "\r\n").append("\r\n").append(this.httpResponse.getBodyData().toString());
                        byte[] bytes = stringBuffer.toString().getBytes(Charset.forName("UTF-8"));
                        ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
                        byteBuffer.put(bytes);
                        byteBuffer.compact();
                        this.socketChannel.write(byteBuffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if(this.socketChannel.isConnected()) {
                            try {
                                this.socketChannel.shutdownOutput();
                                this.socketChannel.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }

        //TODO 1、解析Http体
        private HttpResponse parseHttpRequest(StringBuffer requestData) throws IOException, InterruptedException, ExecutionException {

            //TODO 创建HttpRequest和HttpResponse 并封装数据
            HttpRequest httpRequest = HttpRequest.getInstance();
            HttpResponse httpResponse = HttpResponse.getInstance();
            HttpRequestAnalysisUtil.analysisHttpData(requestData, httpRequest, httpResponse);
            if(httpResponse.getCode().getCode() != 200) { //TODO 解析socket数据后结果code不等于200的请求全部socket返回
                httpResponse.getBodyData().append("文件http请求解析失败");
                return httpResponse;
            }

            log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] ServerSocketChannelHttpRequestHandler | --> 客户端请求信息, header = {}", httpRequest.getRequestHeader());
            if(!"".equals(httpRequest.getBodyData())) {
                String request = BasicUtil.replaceBlank(httpRequest.getBodyData());
                RequestData parseData = JSON.parseObject(request, RequestData.class);

                //TODO 校验文件http请求数据是否满足条件
                if(StringUtil.isNullOrEmpty(parseData.getExec()))
                    httpResponse.getBodyData().append("操作命令[exec]不能为空,");
                if(StringUtil.isNullOrEmpty(parseData.getData()))
                    httpResponse.getBodyData().append("[data]不能为空,");

                if(!StringUtil.isNullOrEmpty(httpResponse.getBodyData().toString())) {
                    httpResponse.getBodyData().toString().substring(0, httpResponse.getBodyData().toString().lastIndexOf(","));
                    return httpResponse;
                }

                if(parseData.getExec().equals(BasicConstant.UPLOAD)) {
                    return uploadFileRequestHandler(parseData, httpResponse);
                } else if(parseData.getExec().equals(BasicConstant.DOWNLOAD)) {
                    return downloadFileRequestHandler(parseData, httpResponse);
                } else if(parseData.getExec().equals(BasicConstant.LINUX_COMMAND)) { //TODO --> 获取服务端文件操作连接情况
                    return linuxCommandRequestHandler(parseData, httpResponse);
                } else {
                    httpResponse.getBodyData().append("请求命令[exec]无效,无法处理");
                }
            } else {
                httpResponse.getBodyData().append("请求数据为空，解析失败");
            }

            return httpResponse;
        }

        //TODO 2、上传文件处理(处理是否一个文件还是多个文件)
        private HttpResponse uploadFileRequestHandler(RequestData requestData, HttpResponse httpResponse) throws InterruptedException, ExecutionException {
            return HttpUploadHandler.httpUploadHandler(requestData, httpResponse);
        }

        //TODO 3、下载文件处理(处理是否一个文件还是多个文件)
        private HttpResponse downloadFileRequestHandler(RequestData requestData, HttpResponse httpResponse) throws InterruptedException, ExecutionException {
            return HttpDownloadHandler.httpDownloadHandler(requestData, httpResponse);
        }

        //TODO 4、linux命令处理
        private HttpResponse linuxCommandRequestHandler(RequestData requestData, HttpResponse httpResponse) throws ExecutionException, InterruptedException {
            return LinuxCommandHandler.linuxCommandHandler(requestData, httpResponse);
        }

    }
}
